package com.baoyouqun.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baoyouqun.API.IM.TencentImService;
import com.baoyouqun.base.utils.ConvertUtils;
import com.baoyouqun.base.utils.ExcelTool;
import com.baoyouqun.base.utils.PhoneNumberValidator;
import com.baoyouqun.domain.Enum.DemandTypeEnum;
import com.baoyouqun.domain.Query.LeopardFriendDistQuery;
import com.baoyouqun.domain.VO.*;
import com.baoyouqun.domain.cmd.BatchLeopardFriendStatusCmd;
import com.baoyouqun.domain.cmd.BatchUserStatusCmd;
import com.baoyouqun.domain.cmd.SingleUserStatusCmd;
import com.baoyouqun.entity.*;
import com.baoyouqun.mapper.*;
import com.baoyouqun.service.CityService;
import com.baoyouqun.service.LeopardFriendService;
import com.baoyouqun.service.UserResourceService;
import com.baoyouqun.service.UserVipTimeService;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class LeopardFriendServiceImpl extends ServiceImpl<UserMapper, User> implements LeopardFriendService {

    /**
     * 时间格式化器（yyyy-MM-dd HH:mm）
     */
    private static final DateTimeFormatter DT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    /**
     * 订单状态：1=已支付（与业务数据库定义一致）
     */
    private static final Byte ORDER_PAID_STATUS = 1;
    /**
     * 订单类型：VIP=会员相关订单（与业务数据库定义一致）
     */
    private static final String ORDER_VIP_TYPE = "VIP";
    /**
     * 动态列表默认分页大小（前500条）
     */
    private static final Long DYNAMIC_PAGE_SIZE = 500L;
    /**
     * 地区树形统计核心实现（参考treeUserCount汇总逻辑）
     */
    // 层级常量（避免魔法值）
    private static final String LEVEL_PROVINCE = "PROVINCE";
    private static final String LEVEL_CITY = "CITY";
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserVipTimeMapper userVipTimeMapper;
    @Resource
    private DemandMapper demandMapper;
    @Resource
    private OrderRecordMapper orderRecordMapper;
    @Resource
    private CityMapper cityMapper;
    @Resource
    private VipLevelMapper vipLevelMapper;
    @Resource
    private TencentImService imService;
    @Resource
    private CityService cityService;

    @Resource
    private VirtualNumberMapper virtualNumberMapper;
    @Resource
    private UserResourceMapper userResourceMapper;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private UserVipTimeService userVipTimeService;
    @Resource
    private UserResourceService userResourceService;

    // -------------------------- 1. 分页搜索（独立实现）--------------------------

    // -------------------------- 2. 单个启用禁用（独立实现）--------------------------
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateSingleUserStatus(SingleUserStatusCmd cmd) {
        // 1. 独立参数校验
        if (StrUtil.isBlank(cmd.getUserId())) {
            return Response.buildFailure("100301", "用户ID不能为空");
        }
        if (cmd.getDisEnable() == null) {
            return Response.buildFailure("100301", "请指定状态（启用/禁用）");
        }

        // 2. 独立查询用户
        User user = userMapper.selectById(cmd.getUserId());
        if (user == null) {
            return Response.buildFailure("100303", "用户不存在");
        }


        // 3. 独立更新状态
        user.setDisEnable(cmd.getDisEnable());
        int updateCount = userMapper.updateById(user);
        if (updateCount > 0) {
            String msg = cmd.getDisEnable() ? "禁用成功" : "启用成功";
            return Response.buildSuccess();
        }
        return Response.buildFailure("100305", "状态更新失败");
    }

    // -------------------------- 3. 批量启用禁用（独立实现）--------------------------
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateBatchUserStatus(BatchUserStatusCmd cmd) {
        // 1. 独立参数校验
        if (CollUtil.isEmpty(cmd.getUserIds())) {
            return Response.buildFailure("100301", "用户ID列表不能为空");
        }
        if (cmd.getDisEnable() == null) {
            return Response.buildFailure("100301", "请指定状态（启用/禁用）");
        }

        // 2. 独立查询所有用户（排除注销）
        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>()
                .in(User::getId, cmd.getUserIds()));
        if (CollUtil.isEmpty(userList)) {
            return Response.buildFailure("100303", "无有效用户可操作");
        }

        // 3. 独立批量更新
        List<String> validUserIds = userList.stream().map(User::getId).collect(Collectors.toList());
        User updateEntity = new User();
        updateEntity.setDisEnable(cmd.getDisEnable());
        int updateCount = userMapper.update(updateEntity, new LambdaQueryWrapper<User>()
                .in(User::getId, validUserIds));

        // 4. 独立返回结果
        String msg = cmd.getDisEnable() ? "批量禁用成功" : "批量启用成功";
        msg += "，共处理" + updateCount + "个用户";
        return Response.buildSuccess();
    }


    // -------------------------- 5. 批量导入豹友（独立实现）--------------------------
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<LeopardFriendImportVO> importLeopardFriends(MultipartFile file) throws IOException {
        LeopardFriendImportVO importVO = new LeopardFriendImportVO();
        List<String> failReasons = new ArrayList<>();
        int successCount = 0;
        int skipCount = 0; // 新增：统计姓名为空的跳过行数

        // 1. 预加载所有城市数据（内存映射）
        List<City> allCities = cityMapper.selectList(null);
        Map<String, City> cityNameMap = allCities.stream()
                .collect(Collectors.toMap(
                        City::getName,
                        city -> city,
                        (existing, replacement) -> existing
                ));

        // 2. 预加载所有已存在的非注销用户手机号
        List<User> existingUsers = userMapper.selectList(
                new LambdaQueryWrapper<User>()
                        .eq(User::getLogout, false)
                        .select(User::getCellphone, User::getId)
        );
        Map<String, String> existingPhoneMap = existingUsers.stream()
                .collect(Collectors.toMap(
                        User::getCellphone,
                        User::getId,
                        (existingId, newId) -> existingId
                ));

        // 3. 解析Excel
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        List<List<Object>> rows = reader.read(1);
        int totalCount = rows.size();
        importVO.setTotalCount(totalCount);

        // 4. 处理Excel数据
        for (int i = 0; i < rows.size(); i++) {
            List<Object> row = rows.get(i);
            int rowNum = i + 2;
            try {
                // 4.1 校验必填字段
                if (row.size() < 2) {
                    failReasons.add("第" + rowNum + "行：数据不完整（需包含姓名、手机号）");
                    continue;
                }
                String name = row.get(0) != null ? row.get(0).toString().trim() : "";
                String cellphone = row.get(1) != null ? row.get(1).toString().trim() : "";

                // 姓名为空时跳过，不计入失败，统计到跳过数量
                if (StrUtil.isBlank(name)) {
                    skipCount++;
                    continue;
                }

                if (StrUtil.isBlank(cellphone) || !ReUtil.isMatch("^1[3-9]\\d{9}$", cellphone)) {
                    failReasons.add("第" + rowNum + "行：手机号格式错误");
                    continue;
                }

                // 4.2 解析可选字段
                String company = row.size() >= 3 ? (row.get(2) != null ? row.get(2).toString().trim() : "") : "";
                String job = row.size() >= 4 ? (row.get(3) != null ? row.get(3).toString().trim() : "") : "";
                String province = row.size() >= 5 ? (row.get(4) != null ? row.get(4).toString().trim() : "") : "";
                String city = row.size() >= 6 ? (row.get(5) != null ? row.get(5).toString().trim() : "") : "";
                String district = row.size() >= 7 ? (row.get(6) != null ? row.get(6).toString().trim() : "") : "";

                // 4.3 匹配cityCode
                String cityCode = null;
                if (StrUtil.isNotBlank(district)) {
                    City matchedCity = cityNameMap.get(district);
                    if (matchedCity != null) {
                        cityCode = matchedCity.getCode();
                    } else {
                        failReasons.add("第" + rowNum + "行：系统中不存在的区县名称「" + district + "」");
                        continue;
                    }
                }

                // 4.4 构建用户对象
                User user = new User();
                user.setName(name);
                user.setCellphone(cellphone);
                user.setExtraPhones(List.of(cellphone));
                user.setCompany(company);
                user.setJob(job);
                user.setProvince(province);
                user.setCity(city);
                user.setDistrict(district);
                user.setCityCode(cityCode);
                user.setBaoZhiHao(PhoneNumberValidator.isValidPhone(cellphone));
                user.setDisEnable(false);
                user.setLogout(false);
                user.setIsPublic(true);

                // 4.5 新增或更新用户
                if (existingPhoneMap.containsKey(cellphone)) {
                    String existingUserId = existingPhoneMap.get(cellphone);
                    user.setId(existingUserId);
                    userMapper.updateById(user);
                } else {
                    user.setId(UUID.randomUUID().toString().replace("-", ""));
                    user.setPassword(passwordEncoder.encode(cellphone));
                    user.setRegisterTime(LocalDateTime.now());
                    userMapper.insert(user);
                    existingPhoneMap.put(cellphone, user.getId());
                }

                successCount++;
            } catch (Exception e) {
                failReasons.add("第" + rowNum + "行：处理异常-" + e.getMessage());
            }
        }

        // 5. 封装结果（执行数量=成功数量，跳过数量=姓名为空行数）
        importVO.setSuccessCount(successCount); // 执行了多少（成功处理的数量）
        importVO.setSkipCount(skipCount);      // 跳过了多少（姓名为空的数量）
        importVO.setFailCount(failReasons.size()); // 失败数量（排除跳过的情况）
        importVO.setFailReasons(failReasons);
        return SingleResponse.of(importVO);
    }


    // -------------------------- 导出工具方法（独立）--------------------------

    /**
     * 导出空Excel（独立实现）
     */
    private void exportEmptyExcel(HttpServletResponse response, String fileName) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter(true);
        setExportResponseHeader(response, fileName);
        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();
    }

    /**
     * 设置导出响应头（独立实现）
     */
    private void setExportResponseHeader(HttpServletResponse response, String fileName) throws IOException {
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        String encodedFileName = URLEncoder.encode(fileName + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")), StandardCharsets.UTF_8);
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        ExcelTool.setExportFileName(response, fileName);

    }


    /**
     * 批量启用/禁用豹友（事务保证）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response batchDisEnable(BatchLeopardFriendStatusCmd cmd) {
        // 1. 参数校验
        if (CollUtil.isEmpty(cmd.getUserIds())) {
            return Response.buildFailure("100301", "用户ID列表不能为空");
        }
        if (cmd.getDisEnable() == null) {
            return Response.buildFailure("100301", "状态不能为空（true=禁用，false=启用）");
        }

        // 2. 批量更新状态
        LambdaQueryWrapper<User> updateWrapper = new LambdaQueryWrapper<User>()
                .in(User::getId, cmd.getUserIds());
        User updateEntity = new User();
        updateEntity.setDisEnable(cmd.getDisEnable());

        boolean success = update(updateEntity, updateWrapper);
        if (success) {
            return Response.buildSuccess();
        }
        return Response.buildFailure("100304", "批量操作失败");
    }

    /**
     * 检查手机号重复（返回重复用户信息）
     */
    @Override
    public SingleResponse<DuplicateCheckVO> checkDuplicateCellphone(String cellphone) {
        // 1. 参数校验
        if (StrUtil.isBlank(cellphone)) {
            return SingleResponse.buildFailure("100301", "手机号不能为空");
        }

        // 2. 查询重复用户
        List<String> duplicateUserIds = getDuplicateUserIdsByCellphone(cellphone);
        DuplicateCheckVO result = new DuplicateCheckVO();
        result.setIsDuplicate(CollUtil.isNotEmpty(duplicateUserIds));

        // 3. 补充重复用户信息
        if (CollUtil.isNotEmpty(duplicateUserIds)) {
            List<User> duplicateUsers = listByIds(duplicateUserIds);
            List<UserVO> duplicateUserVOs = ConvertUtils.copyBeanList(duplicateUsers, UserVO.class);
            result.setDuplicateUserIds(duplicateUserIds);
            result.setDuplicateUsers(duplicateUserVOs);
        } else {
            result.setDuplicateUserIds(new ArrayList<>());
            result.setDuplicateUsers(new ArrayList<>());
        }

        return SingleResponse.of(result);
    }

    /**
     * 根据手机号获取重复用户ID列表（排除注销用户）
     */
    @Override
    public List<String> getDuplicateUserIdsByCellphone(String cellphone) {
        if (StrUtil.isBlank(cellphone)) {
            return new ArrayList<>();
        }

        List<User> users = list(new LambdaQueryWrapper<User>()
                .eq(User::getCellphone, cellphone)
                .eq(User::getLogout, false)); // 排除注销用户

        return users.stream()
                .map(User::getId)
                .collect(Collectors.toList());
    }

    // ======================== 核心统计方法 ========================
    @Override
    public SingleResponse<LeopardFriendCoreStatVO> getCoreStat() {
        LeopardFriendCoreStatVO statVO = new LeopardFriendCoreStatVO();
        LocalDateTime now = LocalDateTime.now();

        // 1. 豹友总人数（非后台、未注销、未禁用）
        Long totalUserCount = userMapper.selectLeopardTotalCount(null, null);
        statVO.setTotalUserCount(totalUserCount);

        // 2. 当前在线人数（优先腾讯IM，降级Sa-Token）
        Set<String> onlineUserIds = imService.getOnlineUserIds();
        // 过滤非豹友用户（仅保留正常用户）
        Long onlineLeopardCount = onlineUserIds.stream().count();
        statVO.setOnlineUserCount(onlineLeopardCount);

        // 3. 开通VIP会员人数（有效会员：endTime>当前时间，startTime<=当前时间）
        Long vipUserCount = userVipTimeMapper.selectVipTotalCount(null, null);
        statVO.setVipUserCount(vipUserCount);

        // 4. 已发布帖子总数（正常状态：status=1，包含“需要资源”和“拥有资源”）
        Long needDemandCount = demandMapper.selectDemandTotalCount(
                DemandTypeEnum.NEED.name(), null, null
        );
        Long haveDemandCount = demandMapper.selectDemandTotalCount(
                DemandTypeEnum.OWN.name(), null, null
        );
        statVO.setTotalDemandCount(Objects.isNull(needDemandCount) ? 0 : needDemandCount
                + (Objects.isNull(haveDemandCount) ? 0 : haveDemandCount));

        // 5. 已开通虚拟通话人数（User表isVirtualCall=true）
        Long virtualCallUserCount = virtualNumberMapper.selectCount(
                new LambdaQueryWrapper<VirtualNumber>()
                        .gt(VirtualNumber::getExpireMinutes, 0)
        );
        statVO.setVirtualCallUserCount(virtualCallUserCount);

        // 6. 累计收入金额（VIP相关已支付订单的实付金额总和）
        List<Object> payAmountList = orderRecordMapper.selectObjs(
                new LambdaQueryWrapper<OrderRecord>()
                        .eq(OrderRecord::getType, ORDER_VIP_TYPE)
                        .eq(OrderRecord::getStatus, ORDER_PAID_STATUS)
                        .select(OrderRecord::getPayAmount)
        );
        BigDecimal totalIncome = payAmountList.stream()
                .map(obj -> (BigDecimal) obj)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        statVO.setTotalIncome(totalIncome);

        return SingleResponse.of(statVO);
    }

    /**
     * 核心实现：地区用户统计（简化入参）
     */
    @Override
    public SingleResponse<List<Tree<String>>> getAreaUserStat() {
        // 查询全国总豹友数（用于计算省级占比）
        Long nationwideTotalUser = userMapper.selectNationwideTotalUserCount();
        nationwideTotalUser = Objects.isNull(nationwideTotalUser) ? 0L : nationwideTotalUser;

        // 获取所有地区树形数据（包含用户数和VIP数）
        List<Tree<String>> treeList = cityService.treeUserVipCount(nationwideTotalUser);

        return SingleResponse.of(treeList);
    }

    // ======================== 需求分布方法 ========================
    @Override
    public SingleResponse<List<Tree<String>>> getDemandDistByType() {
        List<Tree<String>> treeList = cityService.treeDemandCountByType();
        return SingleResponse.of(treeList);
    }

    // ======================== VIP会员动态方法 ========================
    @Override
    public SingleResponse<IPage<VipDynamicVO>> getVipDynamic(LeopardFriendDistQuery query) {
        // 1. 初始化分页对象（仅查询第1页，每页500条，符合“前500位”需求）
        IPage<UserVipTime> vipPage = new Page<>(1, DYNAMIC_PAGE_SIZE);
        LocalDateTime now = LocalDateTime.now();

        // 2. 构建VIP记录查询条件（有效会员+时间范围筛选）
        LambdaQueryWrapper<UserVipTime> vipWrapper = new LambdaQueryWrapper<UserVipTime>()
                .gt(UserVipTime::getEndTime, now)
                .le(UserVipTime::getStartTime, now)
                .orderByDesc(UserVipTime::getCreateTime); // 按开通时间倒序

        // 时间范围筛选（若传入开始/结束时间）
        if (StringUtils.isNotBlank(query.getStartTime())) {
            vipWrapper.ge(UserVipTime::getCreateTime, LocalDateTime.parse(query.getStartTime(), DT_FORMATTER));
        }
        if (StringUtils.isNotBlank(query.getEndTime())) {
            vipWrapper.le(UserVipTime::getCreateTime, LocalDateTime.parse(query.getEndTime(), DT_FORMATTER));
        }

        // 3. 执行分页查询
        vipPage = userVipTimeMapper.selectPage(vipPage, vipWrapper);
        if (CollUtil.isEmpty(vipPage.getRecords())) {
            return SingleResponse.of(new Page<>(1, DYNAMIC_PAGE_SIZE));
        }

        // 4. 批量关联用户信息（避免N+1查询）
        List<String> userIds = vipPage.getRecords().stream()
                .map(UserVipTime::getUserId)
                .collect(Collectors.toList());
        Map<String, User> userMap = userMapper.selectBatchIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, user -> user, (a, b) -> a));

        // 5. 批量关联会员等级信息
        List<String> vipLevelIds = vipPage.getRecords().stream()
                .map(UserVipTime::getVipLevelId)
                .collect(Collectors.toList());
        Map<String, VipLevel> vipLevelMap = vipLevelMapper.selectBatchIds(vipLevelIds).stream()
                .collect(Collectors.toMap(VipLevel::getId, level -> level, (a, b) -> a));

        // 6. 预加载城市数据（用于拼接地区名）
        Map<String, City> cityMap = null;

        // 7. 转换为VIP动态VO（处理脱敏和地区名）
        List<VipDynamicVO> voList = vipPage.getRecords().stream().map(vipTime -> {
            VipDynamicVO vo = new VipDynamicVO();
            User user = userMap.get(vipTime.getUserId());

            if (user != null) {
                // 设置用户名
                vo.setUserName(user.getName());
                // 手机号脱敏（格式：138****5678）
                vo.setCellphoneDesensitized(DesensitizedUtil.mobilePhone(user.getCellphone()));
                // 拼接完整地区名（格式：省-市-区县，如“江西-南昌-青山湖区”）
                vo.setAreaName(getFullAreaName(user, cityMap));
            }

            // 设置会员等级名称（无等级时默认“临时会员”）
            VipLevel vipLevel = vipLevelMap.get(vipTime.getVipLevelId());
            vo.setVipLevelName(vipLevel != null ? vipLevel.getLevelName() : "临时会员");

            // 设置开通时间
            vo.setCreateTime(vipTime.getCreateTime());

            return vo;
        }).collect(Collectors.toList());

        // 8. 组装分页VO结果
        IPage<VipDynamicVO> voPage = new Page<>(
                vipPage.getCurrent(),
                vipPage.getSize(),
                vipPage.getTotal()
        );
        voPage.setRecords(voList);
        return SingleResponse.of(voPage);
    }

    // ======================== 新豹友动态方法 ========================
    @Override
    public SingleResponse<IPage<NewUserDynamicVO>> getNewUserDynamic(LeopardFriendDistQuery query) {
        // 1. 初始化分页对象（仅查询第1页，每页500条）
        IPage<User> userPage = new Page<>(1, DYNAMIC_PAGE_SIZE);

        // 2. 构建新豹友查询条件（正常用户+时间范围筛选）
        LambdaQueryWrapper<User> userWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getBackWorker, false)
                .eq(User::getLogout, false)
                .eq(User::getDisEnable, false)
                .orderByDesc(User::getRegisterTime); // 按注册时间倒序


        // 3. 执行分页查询
        userPage = userMapper.selectPage(userPage, userWrapper);
        if (CollUtil.isEmpty(userPage.getRecords())) {
            return SingleResponse.of(new Page<>(1, DYNAMIC_PAGE_SIZE));
        }


        // 5. 转换为新豹友动态VO（处理脱敏和地区名）
        List<NewUserDynamicVO> voList = userPage.getRecords().stream().map(user -> {
            NewUserDynamicVO vo = new NewUserDynamicVO();
            // 手机号脱敏（格式：138****5678）
            vo.setCellphoneDesensitized(DesensitizedUtil.mobilePhone(user.getCellphone()));
            // 设置注册时间
            vo.setRegisterTime(user.getRegisterTime());
            // 拼接完整地区名（格式：省-市-区县）
            return vo;
        }).collect(Collectors.toList());

        // 6. 组装分页VO结果
        IPage<NewUserDynamicVO> voPage = new Page<>(
                userPage.getCurrent(),
                userPage.getSize(),
                userPage.getTotal()
        );
        voPage.setRecords(voList);
        return SingleResponse.of(voPage);
    }

    // ======================== 导出豹友地区分布Excel ========================
/*
    @Override
    public void exportAreaDistExcel(String cityCode, HttpServletResponse response) throws IOException {
        // 1. 获取地区分布数据（复用查询逻辑）
        SingleResponse<List<AreaUserStatVO>> distResponse = SingleResponse.of(null);


        // 2. 构建Excel导出数据（Map格式适配Hutool）
        List<Map<String, Object>> exportData = new ArrayList<>();


        // 3. 初始化Hutool ExcelWriter（XLSX格式，支持大量数据）
        ExcelWriter writer = ExcelUtil.getWriter(true);
        // 设置表头别名（与Excel列名一一对应）
        writer.addHeaderAlias("areaName", "地区");
        writer.addHeaderAlias("userCount", "豹友数（人）");
//        writer.addHeaderAlias("userRatio", "豹友占比（%）");
        writer.addHeaderAlias("vipUserCount", "已开通VIP人数（人）");
        // 写入数据（true表示带表头）
        writer.write(exportData, true);

        // 4. 设置Excel下载响应头（解决中文乱码+浏览器兼容）
        setExcelResponseHeader(response, "豹友地区分布_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));

        // 5. 输出Excel流（try-with-resources自动关闭流）
        try (ServletOutputStream out = response.getOutputStream()) {
            writer.flush(out, true);
        } finally {
            writer.close(); // 手动关闭ExcelWriter，释放POI资源
        }
    }
*/

    // ======================== 导出需求分布Excel ========================
/*    @Override
    public void exportDemandDistExcel(LeopardFriendDistQuery query, HttpServletResponse response) throws IOException {
        // 1. 获取需求分布数据（复用查询逻辑）
        SingleResponse<List<DemandDistVO>> distResponse = new SingleResponse<>();
        List<DemandDistVO> distVOList = distResponse.getData();

        // 2. 构建Excel导出数据
        List<Map<String, Object>> exportData = new ArrayList<>();
        for (DemandDistVO vo : distVOList) {
            Map<String, Object> row = new HashMap<>();
            row.put("areaName", vo.getAreaName());
            row.put("needResourceCount", vo.getNeedResourceCount());
            row.put("haveResourceCount", vo.getHaveResourceCount());
            row.put("totalCount", vo.getNeedResourceCount() + vo.getHaveResourceCount()); // 需求总数
            exportData.add(row);
        }

        // 3. 初始化Hutool ExcelWriter
        ExcelWriter writer = ExcelUtil.getWriter(true);
        // 设置表头别名
        writer.addHeaderAlias("areaName", "地区");
        writer.addHeaderAlias("needResourceCount", "已发布需要资源数（份）");
        writer.addHeaderAlias("haveResourceCount", "已发布拥有资源数（份）");
        writer.addHeaderAlias("totalCount", "需求总数（份）");
        // 写入数据
        writer.write(exportData, true);

        // 4. 设置响应头
        setExcelResponseHeader(response, "需求分布_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));

        // 5. 输出流
        try (ServletOutputStream out = response.getOutputStream()) {
            writer.flush(out, true);
        } finally {
            writer.close();
        }
    }
 */

    /**
     * 根据城市编码获取地区名（格式：省-市，如“江西-南昌”）
     *
     * @param cityCode 城市编码（如360100）
     * @param cityMap  预加载的城市映射（code→City）
     * @return 拼接后的地区名
     */
    private String getAreaNameByCode(String cityCode, Map<String, City> cityMap) {
        if (StringUtils.isBlank(cityCode) || !cityMap.containsKey(cityCode)) {
            return "未知地区";
        }
        // 获取城市信息
        City city = cityMap.get(cityCode);
        // 获取省份信息（通过城市的parentCode关联）
        City province = cityMap.get(city.getParentCode());
        // 拼接格式：省-市（如“江西-南昌”，无省份时仅返回城市名）
        return (province != null ? province.getName() : "") +
                (province != null ? "-" : "") +
                city.getName();
    }

    // ======================== 工具方法（复用逻辑） ========================

    /**
     * 拼接用户完整地区名（格式：省-市-区县，如“江西-南昌-青山湖区”）
     *
     * @param user    用户实体（含province/city/district编码）
     * @param cityMap 预加载的城市映射（code→City）
     * @return 完整地区名
     */
    private String getFullAreaName(User user, Map<String, City> cityMap) {
        List<String> areaParts = new ArrayList<>();
        // 拼接省份（若有编码且存在于cityMap）
        areaParts.add(user.getProvince());
        // 拼接城市（若有编码且存在于cityMap）
        areaParts.add(user.getCity());
        // 拼接区县（若有编码且存在于cityMap）
        areaParts.add(user.getDistrict());
        // 用“-”连接各部分（无数据时返回空字符串）
        return StringUtils.join(areaParts, "-");
    }

    /**
     * 设置Excel下载响应头（通用方法，解决中文乱码和浏览器兼容）
     *
     * @param response Http响应对象
     * @param fileName 文件名（不含后缀）
     */
    private void setExcelResponseHeader(HttpServletResponse response, String fileName) throws IOException {
        // 设置响应内容类型（XLSX格式）
        response.setContentType("application/vnd.ms-excel;charset=utf-8");        // 2. 生成带时间戳的文件名（避免重复）
        // 编码文件名（解决中文乱码）
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        // 设置下载头（attachment表示下载，inline表示预览）

        ExcelTool.setExportFileName(response, fileName);
        // 禁用缓存（避免浏览器缓存旧文件）
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
    }

    private List<AreaUserStatVO> getAreaUserStatDetail(String cityCode) {
        List<AreaUserStatVO> list = new ArrayList<>();
        // 查询全国总豹友数（用于计算省级占比）
        Long nationwideTotalUser = userMapper.selectNationwideTotalUserCount();
        nationwideTotalUser = Objects.isNull(nationwideTotalUser) ? 0L : nationwideTotalUser;

        // 获取所有地区树形数据（包含用户数和VIP数）
        List<Tree<String>> treeList = cityService.treeUserVipCount(nationwideTotalUser);
        if (CollUtil.isEmpty(treeList)) {
            return list;
        }

        for (Tree<String> tree : treeList) {
            // 处理顶层节点（如果cityCode为空，筛选根节点）
            if (StringUtils.isBlank(cityCode)) {
                // 根节点的parentId通常为null或特定标识（如"0"）
                if (tree.getParentId() == null || "".equals(tree.getParentId())) {
                    addAreaUserStatVO(list, tree);
                }
            } else {
                // 筛选父ID匹配的节点（使用String.equals避免类型问题）
                if (cityCode.equals(tree.getParentId())) {
                    addAreaUserStatVO(list, tree);
                }
            }
        }

        // 2. 处理未绑定地区（无cityCode）的情况，单独添加一行
        // 查询未绑定地区的用户数和VIP数
        Long unboundUserCount = userMapper.selectUnboundAreaUserCount(); // 需新增：查询cityCode为空的用户数
        Long unboundVipCount = userMapper.selectUnboundAreaValidVipCount();   // 需新增：查询cityCode为空的VIP数
        unboundUserCount = Objects.isNull(unboundUserCount) ? 0L : unboundUserCount;
        unboundVipCount = Objects.isNull(unboundVipCount) ? 0L : unboundVipCount;

        // 构建未绑定地区的统计VO
        AreaUserStatVO unboundVO = new AreaUserStatVO();
        unboundVO.setAreaName("未绑定地区"); // 显示名称
        unboundVO.setUserCount(unboundUserCount);
        unboundVO.setVipCount(unboundVipCount);
        // 计算占比（与其他地区保持一致的逻辑，避免除0）
        double proportion = nationwideTotalUser == 0 ? 0 : (unboundUserCount.doubleValue() / nationwideTotalUser) * 100;
        unboundVO.setUserRatio(new BigDecimal(proportion).setScale(2, RoundingMode.HALF_UP)); // 保留2位小数
        list.add(unboundVO); // 单独添加到结果列表

        return list;
    }


    private List<DemandDistVO> getDemandDistByTypeDetail(String cityCode) {
        List<DemandDistVO> list = new ArrayList<>();

        // 获取所有地区树形数据（包含用户数和VIP数）
        List<Tree<String>> treeList = cityService.treeDemandCountByType();
        if (CollUtil.isEmpty(treeList)) {
            return list;
        }

        for (Tree<String> tree : treeList) {
            // 处理顶层节点（如果cityCode为空，筛选根节点）
            if (StringUtils.isBlank(cityCode)) {
                // 根节点的parentId通常为null或特定标识（如"0"）
                if (tree.getParentId() == null || "".equals(tree.getParentId())) {
                    addDemandDistVO(list, tree);
                }
            } else {
                // 筛选父ID匹配的节点（使用String.equals避免类型问题）
                if (cityCode.equals(tree.getParentId())) {
                    addDemandDistVO(list, tree);
                }
            }
        }
        return list;
    }

    /**
     * 封装AreaUserStatVO并添加到列表
     */
    private void addDemandDistVO(List<DemandDistVO> list, Tree<String> tree) {
        DemandDistVO vo = new DemandDistVO();
        // 区域名称从getName()获取（Tree类中定义的名称获取方法）
        vo.setAreaName(tree.getName().toString());
        // 用户数从扩展属性获取（假设键为"userCount"）
        vo.setHaveResourceCount((Long) tree.get("publishResourceCount"));
        // VIP数从扩展属性获取（假设键为"vipCount"）
        vo.setNeedResourceCount((Long) tree.get("publishDemandCount"));
        list.add(vo);
    }

    /**
     * 封装AreaUserStatVO并添加到列表
     */
    private void addAreaUserStatVO(List<AreaUserStatVO> list, Tree<String> tree) {
        AreaUserStatVO vo = new AreaUserStatVO();
        // 区域名称从getName()获取（Tree类中定义的名称获取方法）
        vo.setAreaName(tree.getName().toString());
        // 用户数从扩展属性获取（假设键为"userCount"）
        vo.setUserCount((Long) tree.get("userCount"));
        // VIP数从扩展属性获取（假设键为"vipCount"）
        vo.setVipCount((Long) tree.get("userVipCount"));
        list.add(vo);
    }

    /**
     * 导出豹友地区分布Excel
     */
    @Override
    public void exportAreaDistExcel(String cityCode, HttpServletResponse response) throws IOException {
        // 获取地区分布数据（复用查询逻辑）
        List<AreaUserStatVO> distVOList = getAreaUserStatDetail(cityCode);

        // 构建Excel导出数据
        List<Map<String, Object>> exportData = distVOList.stream().map(vo -> {
            Map<String, Object> row = new HashMap<>();
            row.put("areaName", vo.getAreaName());
            row.put("userCount", vo.getUserCount());
            row.put("vipUserCount", vo.getVipCount());
            return row;
        }).collect(Collectors.toList());

        // 导出Excel
        exportExcel(response, exportData, "豹友地区分布",
                new String[]{"areaName", "userCount", "vipUserCount"},
                new String[]{"地区", "豹友数（人）", "已开通VIP人数（人）"});
    }


    /**
     * 导出已发布需求分布Excel
     */
    @Override
    public void exportDemandDistExcel(LeopardFriendDistQuery query, HttpServletResponse response) throws IOException {
        // 获取需求分布数据
        List<DemandDistVO> distVOList = getDemandDistByTypeDetail(query.getCityCode());

        // 构建Excel导出数据
        List<Map<String, Object>> exportData = distVOList.stream().map(vo -> {
            Map<String, Object> row = new HashMap<>();
            row.put("areaName", vo.getAreaName());
            row.put("needResourceCount", vo.getNeedResourceCount());
            row.put("haveResourceCount", vo.getHaveResourceCount());
            return row;
        }).collect(Collectors.toList());

        // 导出Excel
        exportExcel(response, exportData, "需求分布",
                new String[]{"areaName", "needResourceCount", "haveResourceCount"},
                new String[]{"地区", "已发布需要资源数（份）", "已发布拥有资源数（份）"});
    }


    /**
     * 导出新豹友动态Excel
     */
    @Override
    public void exportNewUserDynamicExcel(LeopardFriendDistQuery query, HttpServletResponse response) throws IOException {
        // 获取新豹友动态数据
        SingleResponse<IPage<NewUserDynamicVO>> dynamicResponse = getNewUserDynamic(query);
        IPage<NewUserDynamicVO> userPage = dynamicResponse.getData();
        List<NewUserDynamicVO> dataList = userPage.getRecords();

        // 构建导出数据
        List<Map<String, Object>> exportData = dataList.stream().map(vo -> {
            Map<String, Object> row = new HashMap<>();
            row.put("cellphoneDesensitized", vo.getCellphoneDesensitized());
            row.put("registerTime", vo.getRegisterTime() != null ? vo.getRegisterTime().format(DT_FORMATTER) : "");
            return row;
        }).collect(Collectors.toList());

        // 导出Excel
        exportExcel(response, exportData, "新豹友动态",
                new String[]{"cellphoneDesensitized", "registerTime"},
                new String[]{"手机号", "注册时间"});
    }

    /**
     * 导出VIP会员动态Excel
     */
    @Override
    public void exportVipDynamicExcel(LeopardFriendDistQuery query, HttpServletResponse response) throws IOException {
        // 获取VIP动态数据
        SingleResponse<IPage<VipDynamicVO>> dynamicResponse = getVipDynamic(query);
        IPage<VipDynamicVO> vipPage = dynamicResponse.getData();
        List<VipDynamicVO> dataList = vipPage.getRecords();

        // 构建导出数据
        List<Map<String, Object>> exportData = dataList.stream().map(vo -> {
            Map<String, Object> row = new HashMap<>();
            row.put("userName", vo.getUserName());
            row.put("cellphoneDesensitized", vo.getCellphoneDesensitized());
            row.put("areaName", vo.getAreaName());
            row.put("vipLevelName", vo.getVipLevelName());
            row.put("createTime", vo.getCreateTime() != null ? vo.getCreateTime().format(DT_FORMATTER) : "");
            return row;
        }).collect(Collectors.toList());

        // 导出Excel
        exportExcel(response, exportData, "开通VIP会员动态",
                new String[]{"已开通VIP会员", "userName", "cellphoneDesensitized", "areaName", "vipLevelName", "createTime"},
                new String[]{"已开通VIP会员", "用户名", "手机号", "地区", "会员等级", "开通时间"});
    }

    /**
     * 统一Excel导出方法
     */
    private void exportExcel(HttpServletResponse response, List<Map<String, Object>> data,
                             String menuName, String[] keys, String[] headers) throws IOException {
        // 初始化Hutool ExcelWriter
        ExcelWriter writer = ExcelUtil.getWriter(true);

        // 设置表头别名
        for (int i = 0; i < keys.length; i++) {
            writer.addHeaderAlias(keys[i], headers[i]);
        }

        // 写入数据
        writer.write(data, true);

        // 设置响应头和文件名（菜单名称+时间）
        String timeSuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
        String fileName = menuName + "_" + timeSuffix;
        setExcelResponseHeader(response, menuName);

        // 输出流
        try (ServletOutputStream out = response.getOutputStream()) {
            writer.flush(out, true);
        } finally {
            writer.close();
        }
    }

    /**
     * Excel导出临时VO（独立内部类）
     */
    @Data
    private static class ExcelExportVO {
        private String name;
        private String sex;
        private String cellphone;
        private String industry;
        private String company;
        private String job;
        private String education;
        private String region;
        private String ownResource;
        private String memberLevel;
        private String disEnableDesc;
    }
}